//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <elapi.h>
#include <stdlib.h>
#include <aura.h>
#include "marshal.h"

typedef struct _CHANNEL{
    wchar_t * name;
    Aura_ChannelId id;
    Boolean used;
    Aura_Char reserved[3];
} CHANNEL;

PVoid channelPoolCs = NULL;

static CHANNEL* channel = NULL;
static int channel_num = 0;

#define NALLOC  10

EXTERN_C ECode CDECL InitChannelPool()
{
    return (ECode)AuraCriticalSection_Create(&channelPoolCs);
}

EXTERN_C void CDECL DestroyChannelPool()
{
    AuraCriticalSection_Destroy(channelPoolCs);
    return ;
}

/* alloc more entries in the channel[] array */
static int
channel_alloc(void)
{
    int i;

    if (channel == NULL)
        channel = (CHANNEL*)Aura_malloc(NALLOC * sizeof(CHANNEL));
    else
        channel = (CHANNEL*)Aura_realloc(channel, (channel_num+NALLOC)*sizeof(CHANNEL));

    if (channel == NULL) {
        Aura_printf("can't alloc for channel array\n");
        return -1;
    }

    /* initialize the new entries */
    for (i = channel_num; i < channel_num + NALLOC; i++) {
        channel[i].name = NULL;
        channel[i].id = 0;
        channel[i].used = FALSE;
    }

    channel_num += NALLOC;
    return 0;
}

/* Called by loop() when connection request from a new channel arrives. */
static int
channel_pickup(wchar_t* channelName)
{
    int i, ret;
    ECode ec;

    if (channel == NULL) {      /* first time we're called */
        ret = channel_alloc();
        if (-1 == ret) {
            return -1;
        }
    }

    while (1) {
        for (i = 0; i < channel_num; i++) {
            if (NULL != channel[i].name && !channel[i].used) {      /* find an available entry */
                if (!wcscmp(channelName, channel[i].name)) {
                    channel[i].used = TRUE;
                    return i;  /* return index in channel[] array */
                }
            }
        }

        for (i = 0; i < channel_num; i++) {
            if (!channel[i].used && NULL == channel[i].name) {      /* find an unused entry */
                channel[i].name = (wchar_t*)Aura_malloc(sizeof(wchar_t) * CHANNELNAME_MAXLENTH);
                if (NULL == channel[i].name) {
                    Aura_printf("Channel_pickup malloc failed!\n");
                    return -1;
                }
                wcscpy(channel[i].name, channelName);
                ec = (ECode)AuraIpcChannel_Connect((Aura_PWChar)(const wchar_t *)channelName, &channel[i].id);
                if (FAILED(ec)) {
                    Aura_printf("AuraIpcChannel_Connect failed!\n");
                    return -1;
                }
                channel[i].used = TRUE;
                return i;  /* return index in channel[] array */
            }
        }

        /* channel array full, time to realloc for more */
        ret = channel_alloc();
        if (-1 == ret)
            return -1;
    }

    return -1;
}

EXTERN_C Aura_ChannelId getChannel(wchar_t* channelName)
{
    int i;

    AuraCriticalSection_Enter(channelPoolCs);
    i = channel_pickup(channelName);
    AuraCriticalSection_Leave(channelPoolCs);

    if (-1 == i)
        return (Aura_ChannelId)-1;
    else
        return channel[i].id;
}

EXTERN_C void releaseChannel(Aura_ChannelId id)
{
    int i;

    AuraCriticalSection_Enter(channelPoolCs);
    for (i = 0; i < channel_num; i++) {
        if (id == channel[i].id) {
            channel[i].used = FALSE;
            break;
        }
    }
    AuraCriticalSection_Leave(channelPoolCs);
}

EXTERN_C void destroyChannel(Aura_ChannelId id)
{
    int i;

    AuraCriticalSection_Enter(channelPoolCs);
    for (i = 0; i < channel_num; i++) {
        if (id == channel[i].id) {
            Aura_free(channel[i].name);
            AuraIpcChannel_Disconnect(channel[i].id);
            channel[i].used = FALSE;
            channel[i].name = NULL;
            channel[i].id = 0;
            break;
        }
    }
    AuraCriticalSection_Leave(channelPoolCs);
}

EXTERN_C void releaseChannelPool(void)
{
    int i;

    AuraCriticalSection_Enter(channelPoolCs);
    for (i = 0; i < channel_num; i++) {
        if (channel[i].used) {
            Aura_free(channel[i].name);
            AuraIpcChannel_Disconnect(channel[i].id);
        }
    }
    AuraCriticalSection_Leave(channelPoolCs);

    if (NULL != channel) {
        Aura_free(channel);
        channel = NULL;
    }
}
